/*
 *  main.c
 *  Asset modeling
 *
 *  Created by Peeter Meos on 27.02.11.
 *  Copyright 2011 NC3A CAT1. All rights reserved.
 *
 */

#include <stdio.h>

#include "io.h"
#include "math.h"

#ifndef QUEUE_H
	#define QUEUE_H
	#include "queue.h"
#endif

#ifndef CONST_H
	#define CONST_H
	#include "const.h"
#endif

#define SIM_LEN 240.0		// Length of the simulation in hours

#define NUM_RW 5			// RW assets
	
#define NUM_AM 1			// Ground ambulance asset

#define NUM_UNITS 3			// Number of combat units

#define CAS_RATE 0.083 		// The rate is 8.3% daily - AD85-8 Bde level total rate

// #define DEBUG
// #define CAS_DATA

int main (int argc, const char * argv[]) {
    
	double clock;
	T_EVENT *event_queue;
	T_UNIT	*units[10];
	T_UNIT  *role2;
	
	T_ASSET *assets[20 + 20];
	
	T_EVENT *event;
	T_CASUALTY *casualty;
	
	char	*s;
	char	i;
	char	as_avail;
	
	double	dist;
	
	double	num_cas;
	
	double	sum_wait_medevac;
	double	sum_total_time;
	
	char	command_line;
	char	c_units;
	double	c_dist;
	char	c_rw;
	char	c_am;

	
	clock = 0.0;
	
	if(argc > 1)
	{ 
		// Reading from command line
		// num_units (max 10) distance (40 .. 120) num_rw (max 20) num_am (max 20)

		c_units = atoi(argv[1]);
		
		c_dist = atof(argv[2]);
		
		c_rw = atoi(argv[3]);
		c_am = atoi(argv[4]);

		
		command_line = 1;
	} else {
		command_line = 0;
	}
	
	
/*
    command_line = 1;
	c_units = 2;
	c_dist = 70.0;
	c_rw	= 5;
	c_am	= 1;
*/
	
	sum_wait_medevac = 0.0;
	sum_total_time = 0.0;
	
	clock = 0.0;
	num_cas = 0.0;
	
	event_queue = NULL;
	
	// We have NUM_UNITS combat units (lat long men name FW_NOT_POSSIBLE unit_id)
	if (command_line == 0){
		units[0] = init_unit(36.82, 7.8, 750, "Bn 1", FW_NOT_POSSIBLE, 0);
		units[1] = init_unit(36.91, 7.76, 750, "Bn 2", FW_NOT_POSSIBLE, 1);
		units[2] = init_unit(36.47, 7.43, 750, "Bn 3", FW_NOT_POSSIBLE, 2);
		
		// NUM_RW amount of helicopters at El-Tarf, currently other locations are not possible
		for(i=0;i<NUM_RW;i++)
		{
			assets[i] = init_asset(RW_ASSET, 36.77, 8.32, 40 + i);
		}
		
		// Number of ground ambulances at El-Tarf, currently other locations are not possible
		for(i=NUM_RW;i<NUM_RW + NUM_AM;i++)
		{
			assets[i] = init_asset(AM_ASSET, 36.77, 8.32, 40 + i);
		}
		
		c_units = NUM_UNITS;
		c_rw	= NUM_RW;
		c_am	= NUM_AM;
		
	}else{
		
		for(i=0;i<c_units;i++)
		{
			asprintf(&s, "Bn %d", i);
			units[i] = init_unit(36.82, 7.8, 750, s, FW_NOT_POSSIBLE, i);
		}
			
		for(i=0;i<c_rw;i++)
		{
			assets[i] = init_asset(RW_ASSET, 36.77, 8.32, 40 + i);
		}
		
		for(i=c_rw;i<c_rw + c_am;i++)
		{
			assets[i] = init_asset(AM_ASSET, 36.77, 8.32, 40 + i);
		}
		
	}
	
	// printf("%d %f %d %d\n", c_units, c_dist, c_rw, c_am);
	
	// One role 2 at El-Tarf
	role2 = init_unit(36.77, 8.32, 0,  "El-Tarf", FW_NOT_POSSIBLE, 30);
	
	
	// Start dying ie. initialise the queues
	for(i=0;i<c_units;i++)
		push_event(&event_queue, init_event(E_CASUALTY, clock + exp_dist(units[i]->men * CAS_RATE / 24.0), units[i]->unit_id));
	
	if(command_line==0){
		pr_log(clock, "Initalising the model");
		
		asprintf(&s, "The model includes %d RW MEDEVAC units", NUM_RW);
		pr_log(clock, s);
		
		asprintf(&s, "The model includes %d combat battalions", NUM_UNITS);
		pr_log(clock, s);
		
		asprintf(&s, "The daily attrition rate will be %f percent", CAS_RATE * 100.0);
		pr_log(clock, s);
		
		pr_log(clock, "The model includes one Role 2 hospital");
		
		asprintf(&s, "The model runs for %f hours", SIM_LEN);
		pr_log(clock, s);		
	}
	/*
	 * Simulation main loop
	 */
	
	if (command_line==0)
		pr_log(clock, "Init done, start main loop");
	
	while ((clock < 240.0) && (event_queue != NULL))
	{
		event = pop_event(&event_queue);
		if (event == NULL)
			break;
		
		// event_queue = event->next;
		clock = event->time;
		
		switch(event->type)
		{
			case (E_CASUALTY):
#ifdef DEBUG
				asprintf(&s, "Casualty occurred at unit %s", units[event->unit_id]->name);
				pr_log(clock, s);
#endif
				
				// Man down. Create new casualty entity and increase casualty counter at the respective unit
				
				num_cas = num_cas + 1.0;
				units[event->unit_id]->men = units[event->unit_id]->men - 1;
				
				units[event->unit_id]->casualties = push_cas(units[event->unit_id]->casualties, 
															 init_casualty(CAS_BS, units[event->unit_id]->unit_id, clock));
				units[event->unit_id]->num_casualties++;

#ifdef DEBUG
				asprintf(&s, "This unit %d has %d casualties", event->unit_id, units[event->unit_id]->num_casualties);
				pr_log(clock, s);
				pr_log(clock, "Scheduling next casualty");
#endif
				
				// Schedule next casualty arrival at that unit
				push_event(&event_queue, init_event(E_CASUALTY, clock + exp_dist(units[event->unit_id]->men * CAS_RATE / 24.0), 
													event->unit_id));
				
#ifdef DEBUG
				pr_log(clock, "Requesting MEDEVAC");
#endif			
				
				// First, check if MEDEVAC is already heading towards the unit
				
				as_avail = 0;
				for(i=0;i<c_rw+c_am;i++)
					if (assets[i]->destination == event->unit_id)
						as_avail++;
				
				if (as_avail * 3 < units[event->unit_id]->num_casualties)
				{
					// Additional asset neccessary at this point
				
					// Look for available assets
					// Here RW vs GA tasking needs to be inserted right now if RW is not available ambulance will be used
					// Additionally in case of more than one Role 2's there needs to be a time comparison
					
					as_avail = 100;
					for(i=0;i<c_rw+c_am;i++)
					{
						if(assets[i]->destination == 100)
						{
							as_avail = i;
							break;
						}
					}
					
					if (as_avail == 100){
						clock = clock;
#ifdef DEBUG	
						pr_log(clock, "No available assets!");
#endif
					}else{
						// Asset "as_avail" will be tasked
						asprintf(&s, "Asset no %d will be tasked to fly to unit %d", as_avail, event->unit_id);
#ifdef DEBUG
						pr_log(clock, s);
#endif
						
						assets[as_avail]->destination = event->unit_id;
						
						// Schedule arrival time
						
						if(command_line == 0)
						{
							dist = distance(units[event->unit_id]->loc_x, units[event->unit_id]->loc_y,
											assets[as_avail]->loc_x, assets[as_avail]->loc_y);
						}else{
							dist = c_dist;
						}
						
#ifdef DEBUG						
						asprintf(&s, "Distance to unit %d is %f km, MEDEVAC will arrive in %f hrs", 
								 event->unit_id, dist, (RW_SEEOFF / 60.0) + dist / assets[as_avail]->speed);
						pr_log(clock, s);
#endif
						
						push_event(&event_queue, init_event(E_MEDARR, clock + (RW_SEEOFF / 60.0) + (dist / assets[as_avail]->speed), 
															assets[as_avail]->asset_id));
						
						// printf("%d %f\n",  as_avail, assets[as_avail]->speed);
					}
				
				}else{
					clock = clock;
#ifdef DEBUG
					
					pr_log(clock, "MEDEVAC already on its way");
#endif
				}
				break;
				
			case (E_MEDARR):
				// MEDEVAC has arrived
				
#ifdef DEBUG				
				asprintf(&s, "Asset no %d has arrived to unit %d", event->unit_id - 40, assets[event->unit_id - 40]->destination);
				pr_log(clock, s);
#endif
				
				// Load RW_CAPACITY - 1 casualties
				i=0;
				while((i<RW_CAPACITY) && (units[assets[event->unit_id - 40]->destination]->num_casualties > 0))
				{
					units[assets[event->unit_id - 40]->destination]->num_casualties--;
					casualty = pop_cas(&units[assets[event->unit_id - 40]->destination]->casualties);
											  
					casualty->medevac_time = clock;
					
					casualty->next = NULL;
											  
					assets[event->unit_id - 40]->casualties = push_cas(assets[event->unit_id - 40]->casualties, casualty);
																	
					i++;
				}
				
#ifdef DEBUG																					  
				asprintf(&s, "Asset no %d loaded %d casualties at %s", event->unit_id - 40, i, 
						 units[assets[event->unit_id - 40]->destination]->name);
				pr_log(clock, s);
#endif
								
				
				// Fly back to Role 2
			
				if (command_line == 0){
					dist = distance(role2->loc_x, role2->loc_y, units[assets[event->unit_id - 40]->destination]->loc_x,
									units[assets[event->unit_id - 40]->destination]->loc_y);
				}else{
					dist = c_dist;
				}
											  
				assets[event->unit_id - 40]->destination = role2->unit_id;							  
				
				asprintf(&s, "Distance to role 2 is %f km, MEDEVAC will arrive in %f hrs", dist, (RW_SEEOFF / 60.0) + 
						 dist / assets[event->unit_id - 40]->speed);
#ifdef DEBUG
				pr_log(clock, s);
#endif
											  
				push_event(&event_queue, init_event(E_MEDROL2, clock + (RW_SEEOFF / 60.0) + (dist / assets[event->unit_id - 40]->speed), 
																 assets[event->unit_id - 40]->asset_id));
											  
				break;
			case (E_MEDROL2):
				
#ifdef DEBUG
				pr_log(clock, "Arrival to Role 2");
#endif				
				
				while(assets[event->unit_id - 40]->casualties != NULL)
				{
					casualty = pop_cas(&assets[event->unit_id - 40]->casualties);
					casualty->role2_time = clock;
#ifdef DEBUG				
					pr_log(clock, "Casualty arrived at role 2");
#endif
					
#ifdef CAS_DATA
					asprintf(&s, "Casualty stats: %f : %f : %f", casualty->wounding_time, casualty->medevac_time, casualty->role2_time);
					pr_log(clock, s);
#endif
					
					sum_wait_medevac = sum_wait_medevac	+ (casualty->medevac_time - casualty->wounding_time);
					sum_total_time = sum_total_time + (casualty->role2_time - casualty->wounding_time);
					
					free_casualty(casualty);
				}
								
				// Refuel the helicopter
				assets[event->unit_id - 40]->casualties = NULL;
				
				push_event(&event_queue, init_event(E_REFUEL, clock + (RW_REFUEL / 60.0), assets[event->unit_id - 40]->asset_id));
				
				break;
				
			case (E_REFUEL):
#ifdef DEBUG
				asprintf(&s, "Refueling asset %f", assets[event->unit_id - 40]->asset_id);
				pr_log(clock, s);
#endif
				// Make the asset available
				assets[event->unit_id - 40]->destination = 100;
				
				break;
			default:
				pr_log(clock, "Other event");
				
				break;
		}
		
		free_event(event);
	}
	
	if (command_line==0)
		pr_log(clock, "The run is finished, here are the stats");
	
	sum_total_time = sum_total_time / num_cas;
	sum_wait_medevac = sum_wait_medevac / num_cas;
	
	if(command_line==0)
	{
	asprintf(&s, "Total run time: %f", clock);
	pr_log(clock, s);
	
	asprintf(&s, "Total casualties: %f", num_cas);
	pr_log(clock, s);
	
	asprintf(&s, "Average wait time (hours): %f", sum_wait_medevac);
	pr_log(clock, s);
	
	asprintf(&s, "Average time to Role 2 (hours): %f\n", sum_total_time);
	pr_log(clock, s);
	
	pr_log(clock, "We are done, lets clear up");
	}else{
		printf("%f %f %f %f\n", clock, num_cas, sum_wait_medevac, sum_total_time);
	}
	
	for(i=0;i<c_units;i++)
		free_unit(units[i]);
	
	for(i=0;i<c_rw;i++)
		free_asset(assets[i]);

    return 0;
}
